using Engine.Graphics;
using Engine.Graphics.VertexStructures;
using SharpDX;
using System.Collections.Generic;

namespace Scenario_Editor.Exporter
{
    internal class ExportMesh
    {
        #region Private Fields

        private int cellsX;
        private int cellsY;
        private List<MeshBlock> road;
        private float scale;
        private List<MeshBlock> terrain;
        private List<MeshBlock> unique;

        #endregion Private Fields

        #region Public Constructors

        public ExportMesh(int cellsX, int cellsY, float scale)
        {
            this.scale = scale;
            this.cellsX = cellsX;
            this.cellsY = cellsY;
            terrain = new List<MeshBlock>();
            road = new List<MeshBlock>();
            unique = new List<MeshBlock>();
        }

        #endregion Public Constructors

        #region Public Methods

        public void AddVectorToTerrain(MyOwnVertex.PositionNormalTextured vector, IGameVertex uvCoordinate, int material)
        {
            //Vector2 uvCoordinate = new Vector2(-uvCoordinate2.X, uvCoordinate2.Y);
            bool blockForMaterialIsReady = false;
            for (int block = 0; block < terrain.Count; block++)
            {
                if (this.terrain[block].Material == material)
                {
                    blockForMaterialIsReady = true;
                    terrain[block].AddVertex(vector, uvCoordinate);
                    break;
                }
            }
            if (!blockForMaterialIsReady)
            {
                terrain.Add(new MeshBlock(material, cellsX, cellsY, scale));
                terrain[terrain.Count - 1].AddVertex(vector, uvCoordinate);
            }
        }

        public void AddVectorToUnique(MyOwnVertex.PositionNormalTextured vector, IGameVertex uvCoordinate, int material)
        {
            //Vector2 uvCoordinate = new Vector2(-uvCoordinate2.X, uvCoordinate2.Y);
            bool blockForMaterialIsReady = false;
            for (int block = 0; block < this.unique.Count; block++)
            {
                // Find corect block with the same material.
                if (this.unique[block].Material == material)
                {
                    blockForMaterialIsReady = true;
                    unique[block].AddUniqueVertex(vector, uvCoordinate);
                    break;
                }
            }
            if (!blockForMaterialIsReady)
            {
                this.unique.Add(new MeshBlock(material, cellsX, cellsY, scale));
                this.unique[this.unique.Count - 1].AddUniqueVertex(vector, uvCoordinate);
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void AddMeshObject(Engine.Graphics.Meshobject meshobject)
        {
            var vertices = meshobject.Vertices;
            var verticesWr2Data = meshobject.VerticesWr2Data;
            var indices = meshobject.Indices;
            var indexedMaterials = meshobject.materialsIDs;
            var nPolysForMaterial = meshobject.UsedPolysForMaterial;
            Engine.Graphics.IGameVertex[] data = verticesWr2Data;

            int offset = 0;
            for (int mat = 0; mat < indexedMaterials.Length; mat++)
            {
                for (int pol = 0; pol < nPolysForMaterial[mat]; pol++)
                {
                    IGameVertex wrDataVertex1 = (IGameVertex)data[indices[offset * 3]];
                    IGameVertex wrDataVertex2 = (IGameVertex)data[indices[offset * 3 + 1]];
                    IGameVertex wrDataVertex3 = (IGameVertex)data[indices[offset * 3 + 2]];

                    int index1 = indices[offset * 3];
                    int index2 = indices[offset * 3 + 1];
                    int index3 = indices[offset * 3 + 2];
                    AddVectorToUnique(vertices[index1], verticesWr2Data[index1], indexedMaterials[mat]);
                    AddVectorToUnique(vertices[index2], verticesWr2Data[index2], indexedMaterials[mat]);
                    AddVectorToUnique(vertices[index3], verticesWr2Data[index3], indexedMaterials[mat]);
                    offset++;
                }
            }
        }

        #endregion Internal Methods

        #region Private Structs

        private struct GetUsedMaterialID
        {
            #region Private Fields

            private int freeMaterialID;
            private int[] materialsUsed;
            private string[] surfacenames;
            private List<string> surfaces;

            #endregion Private Fields

            #region Public Constructors

            public GetUsedMaterialID(string[] surfaceNames)
            {
                surfacenames = surfaceNames;
                surfaces = new List<string>();
                materialsUsed = new int[surfaceNames.Length];
                for (int i = 0; i < materialsUsed.Length; i++)
                {
                    materialsUsed[i] = -1;
                }
                freeMaterialID = 0;
            }

            #endregion Public Constructors

            #region Public Properties

            public string[] GetSurfaceList
            {
                get
                {
                    string[] surfaceList = new string[surfaces.Count];
                    surfaces.CopyTo(surfaceList);
                    return surfaceList;
                }
            }

            #endregion Public Properties

            #region Public Methods

            public int GetID(int materialID)
            {
                if (materialsUsed[materialID] < 0)
                {
                    materialsUsed[materialID] = freeMaterialID;
                    freeMaterialID++;
                    surfaces.Add(surfacenames[materialID]);
                }
                return materialsUsed[materialID];
            }

            #endregion Public Methods
        }

        private struct IndexVertex
        {
            #region Private Fields

            private System.Drawing.Point array;
            private int positionInArray;

            #endregion Private Fields

            #region Public Constructors

            public IndexVertex(System.Drawing.Point coordinates, int posInArray)
            {
                array = coordinates;
                positionInArray = posInArray;
            }

            #endregion Public Constructors

            #region Public Properties

            public int index
            {
                get
                {
                    return positionInArray;
                }
            }

            public int X
            {
                get
                {
                    return array.X;
                }
            }

            public int Y
            {
                get
                {
                    return array.Y;
                }
            }

            #endregion Public Properties
        }

        private struct MeshBlock
        {
            #region Private Fields

            private int cellsX;
            private int cellsY;
            private List<IndexVertex> indices;
            private List<int> indicesForExport;
            private int materialID;
            private float scale;
            private VectorBlock[,] vectorBlock;

            #endregion Private Fields

            #region Public Constructors

            public MeshBlock(int MaterialID, int x, int y, float scale)
            {
                this.scale = scale;
                cellsX = x;
                cellsY = y;
                materialID = MaterialID;
                vectorBlock = new VectorBlock[x, y];
                for (int iX = 0; iX < cellsX; iX++)
                {
                    for (int iY = 0; iY < cellsY; iY++)
                    {
                        vectorBlock[iX, iY].initalisize();
                    }
                }

                indices = new List<IndexVertex>();
                indicesForExport = new List<int>();
            }

            #endregion Public Constructors

            #region Public Properties

            public int Material
            {
                get
                {
                    return this.materialID;
                }
            }

            public int nPolygons
            {
                get
                {
                    return this.indices.Count / 3;
                }
            }

            #endregion Public Properties

            #region Public Methods

            public void AddUniqueVertex(MyOwnVertex.PositionNormalTextured vector, IGameVertex uvCoordinate)
            {
                System.Drawing.Point coordinates = GetCoordinates(vector.Position);
                this.indices.Add(new IndexVertex(coordinates, vectorBlock[coordinates.X, coordinates.Y].Count));
                this.vectorBlock[coordinates.X, coordinates.Y].AddVertex(vector, uvCoordinate);
            }

            public void AddVertex(MyOwnVertex.PositionNormalTextured vector, IGameVertex uvCoordinate)
            {
                System.Drawing.Point coordinates = GetCoordinates(vector.Position);

                bool isVertexInArray = false;

                //if(coordinates == new Point(2,2))
                //{
                //}

                List<MyOwnVertex.PositionNormalTextured> positions = vectorBlock[coordinates.X, coordinates.Y].GetPositions();
                List<IGameVertex> uv = vectorBlock[coordinates.X, coordinates.Y].GetUVCoordinates();
                for (int i = positions.Count - 1; i >= 0; i--)
                {
                    if (positions[i].GetHashCode() == vector.GetHashCode())
                    {
                        if (uv[i].GetHashCode() == uvCoordinate.GetHashCode())
                        {
                            this.indices.Add(new IndexVertex(coordinates, i));
                            isVertexInArray = true;
                            break;
                        }
                        else
                        {
                        }
                    }
                }
                if (!isVertexInArray)
                {
                    this.indices.Add(new IndexVertex(coordinates, vectorBlock[coordinates.X, coordinates.Y].Count));
                    this.vectorBlock[coordinates.X, coordinates.Y].AddVertex(vector, uvCoordinate);
                    //this.uvCoordinates.Add(uvCoordinate);
                }
            }

            #endregion Public Methods

            #region Internal Methods

            internal List<int> GetIndices()
            {
                return this.indicesForExport;
            }

            internal List<IGameVertex> GetUVCoordinates()
            {
                List<IGameVertex> uvCoordinates = new List<IGameVertex>();
                for (int i = 0; i < this.indices.Count; i++)
                {
                    bool isUsed = false;
                    IGameVertex coordinate = this.vectorBlock[this.indices[i].X, this.indices[i].Y].GetUVCoordinate(this.indices[i].index, ref isUsed);

                    if (!isUsed)
                    {
                        uvCoordinates.Add(coordinate);
                    }
                    //else
                    //{
                    //    for (int c = 0; c < uvCoordinates.Count; c++)
                    //    {
                    //        if (coordinate == uvCoordinates[c])
                    //        {
                    //            //throw new Exception("JSDAFASD");
                    //        }

                    //    }
                    //}
                }

                return uvCoordinates;
            }

            internal List<Vector3> GetVertexArrayForExport()
            {
                List<Vector3> vertices = new List<Vector3>();
                int used = 0;
                int usedNot = 0;
                for (int i = 0; i < this.indices.Count; i++)
                {
                    bool isUsed = false;
                    Vector3 position = this.vectorBlock[this.indices[i].X, this.indices[i].Y].GetPosition(this.indices[i].index, ref isUsed);

                    if (!isUsed)
                    {
                        usedNot++;
                        indicesForExport.Add(vertices.Count);
                        vertices.Add(position);
                    }
                    else
                    {
                        used++;
                        for (int v = vertices.Count - 1; v >= 0; v--)
                        {
                            if (vertices[v] == position)
                            {
                                indicesForExport.Add(v);
                                break;
                            }
                        }
                    }
                }
                for (int i = 0; i < vertices.Count; i++)
                {
                    vertices[i] = vertices[i] * scale;
                }
                return vertices;
            }

            #endregion Internal Methods

            #region Private Methods

            private System.Drawing.Point GetCoordinates(Vector3 vector)
            {
                float sectorsize = 102.4f;
                float halfSizeX = sectorsize * cellsX * 0.5f;
                float halfSizeY = sectorsize * cellsY * 0.5f;
                System.Drawing.Point coordinates = new System.Drawing.Point((int)((vector.X + halfSizeX) / sectorsize), (int)((vector.Z + halfSizeY) / sectorsize));

                if (coordinates.X >= this.cellsX)
                {
                    coordinates.X = this.cellsX - 1;
                }
                if (coordinates.Y >= this.cellsY)
                {
                    coordinates.Y = this.cellsY - 1;
                }

                if (coordinates.X < 0)
                {
                    coordinates.X = 0;
                }
                if (coordinates.Y < 0)
                {
                    coordinates.Y = 0;
                }

                return coordinates;
            }

            #endregion Private Methods
        }

        private struct VectorBlock
        {
            #region Private Fields

            private List<bool> isPositionExportet;
            private List<bool> isUVPositionExportet;
            private List<MyOwnVertex.PositionNormalTextured> positions;
            private List<IGameVertex> uvCoordinates;

            #endregion Private Fields

            #region Public Properties

            public int Count
            {
                get
                {
                    return positions.Count;
                }
            }

            #endregion Public Properties

            #region Public Methods

            public void AddVertex(MyOwnVertex.PositionNormalTextured position, IGameVertex uvCoordinate)
            {
                positions.Add(position);
                uvCoordinates.Add(uvCoordinate);
                isPositionExportet = new List<bool>();
                isUVPositionExportet = new List<bool>();

                for (int i = 0; i < positions.Count; i++)
                {
                    isPositionExportet.Add(false);
                    isUVPositionExportet.Add(false);
                }
            }

            #endregion Public Methods

            #region Internal Methods

            internal List<bool> GetISExportet()
            {
                return isPositionExportet;
            }

            internal Vector3 GetPosition(int positionInArray, ref bool isUsed)
            {
                //if (isPositionExportet[p])
                //{
                //    return Vector3.Empty;
                //}
                //else
                //{
                isUsed = isPositionExportet[positionInArray];
                isPositionExportet[positionInArray] = true;
                return this.positions[positionInArray].Position;
                //}
            }

            internal List<MyOwnVertex.PositionNormalTextured> GetPositions()
            {
                return positions;
            }

            internal IGameVertex GetUVCoordinate(int index, ref bool isUsed)
            {
                isUsed = isUVPositionExportet[index];
                isUVPositionExportet[index] = true;
                return this.uvCoordinates[index];
            }

            internal List<IGameVertex> GetUVCoordinates()
            {
                return uvCoordinates;
            }

            internal void initalisize()
            {
                positions = new List<MyOwnVertex.PositionNormalTextured>();
                uvCoordinates = new List<IGameVertex>();
                isPositionExportet = new List<bool>();
                isUVPositionExportet = new List<bool>();
            }

            #endregion Internal Methods
        }

        #endregion Private Structs
    }
}